/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.server.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Set;

import com.sinai.mshab.client.ui.Key;

/**
 * This class provides a convenient way to create safe SQL update queries. This
 * class utilizes {@link PreparedStatement} to insure that user input is made
 * safe. Furthermore, the class automates SQL creation and allows programmers to
 * avoid writing SQL that depends on a single database schema as the SQL query
 * can be made dynamically.
 * 
 * @author Arthur Kalmenson
 */
public class SQLUpdate extends SQLObject {

	/**
	 * The name of table where information should be updates.
	 */
	private String tableName;

	/**
	 * Creates a new SQLUpdate object that utilizes the {@link Connection} conn
	 * and sets this SQLUpdate's table name to tableName.
	 * 
	 * @param conn
	 *            the connection to the database.
	 * @param tableName
	 *            the database table where information will be entered.
	 */
	public SQLUpdate(Connection conn, String tableName) {
		super.connection = conn;

		if (super.invalidArgument(tableName)) {
			throw new IllegalArgumentException(
					"The table name must be instantiated and not empty");
		} else {
			this.tableName = tableName;
		}

	}

	/**
	 * Adds fieldName and value to this SQLUpdate. Note that if the fieldName is
	 * null or empty, an {@link IllegalArgumentException} will be thrown.
	 * 
	 * @param fieldName
	 *            the name of a field in the database.
	 * @param value
	 *            the value the field should take.
	 */
	public void add(String fieldName, String value) {
		super.add(tableName, fieldName, value);
	}

	/**
	 * Adds a new filter to the SQL statement. The filter (the WHERE clause)
	 * will be appended to any existing conditions. Therefore, if the addWhere
	 * recieves "tableA", "fieldA", "=" and "val", then the SQL query that it
	 * will yield will contain "... WHERE ... tableA.fieldA = val ...". Note
	 * that the operator must be a regular SQL operator otherwise a
	 * {@link RuntimeException} will be thrown.
	 * 
	 * @param fieldName
	 *            the field which we will use as a filter.
	 * @param value
	 *            the value which we want to field to have.
	 * @param operator
	 *            the operator between the field name and the value.
	 */
	public void addWhere(String fieldName, String value, String operator) {
		super.addWhere(tableName, fieldName, value, operator);
	}
	
	public void addWhere(Key dbKeys) {
		super.addWhere(dbKeys);
	}
	
	public void addWhere(DbFilterGroup group) {
		super.addWhere(group);
	}
	
	/**
	 * Returns the {@link PreparedStatement} constructed by
	 * @throws SQLException 
	 */
	public PreparedStatement getPreparedStatement() throws SQLException {
		PreparedStatement update = null;

		if (dbFields == null || dbFields.isEmpty()) {
			return update;
		}

		// make the table section.
		String sql = "UPDATE " + tableName + " SET";

		// put the question marks for the values that fields should take.
		sql += createUpdateFieldUnknowns(dbFields);

		// construct the where clause.
		sql += createWhereUnknowns(false);

		// prepare the statement
		update = connection.prepareStatement(sql);

		int fillIndex = 1;

		// fill in required information.
		fillIndex = fillUnknowns(update, dbFields, fillIndex);
		fillIndex = fillUnknowns(update, dbFilters.getFlatSet(), fillIndex);

		return update;
	}

	public int execute() throws SQLException {
		return super.executeUpdate();
	}

	/**
	 * Creates the correct number of " <field> = ? " for the UPDATE statement.
	 * This is utilized.
	 * 
	 * @param fieldToValue
	 * @return
	 */
	private static String createUpdateFieldUnknowns(Set fieldToValue) {

		String sql = "";

		// go through the fields in fieldToValue and construct the appropriate
		// SQL.
		Iterator fields = fieldToValue.iterator();
		int index = 0;
		while (fields.hasNext()) {

			DbField field = (DbField) fields.next();
			sql += " " + field.getField() + " = ?";

			// if we're not in the last section, add a comma
			if (index != fieldToValue.size() - 1) {
				sql += ",";
			}
			index++;
		}

		return sql;
	}
}
